package failover

import (
	"JK-Junior-Go-Engineer-Camp/webook/internal/service/sms"
	"context"
	"errors"
	"log"
	"sync/atomic"
)

// FailOverSMSService 短息服务的 failover（故障转移） 装饰器 （方案一）
type FailOverSMSService struct {
	svcs []sms.Service // 候选的服务商

	// 实现二（V1）的字段
	idx uint64 // 记录当前服务商下标
}

func NewFailOverSMSService(svcs []sms.Service) *FailOverSMSService {
	return &FailOverSMSService{
		svcs: svcs,
	}
}

// Send 实现一：直接搞一个轮询
func (f *FailOverSMSService) Send(ctx context.Context,
	templateId string, args []string, number ...string) error {
	for _, svc := range f.svcs {
		err := svc.Send(ctx, templateId, args, number...)
		if err == nil {
			return nil
		}
		// 打日志
		log.Println(err)
	}
	return errors.New("轮询了所有短信服务商，都发送失败了")
}

// SendV1 实现二：设置一个起始轮询位置
func (f *FailOverSMSService) SendV1(ctx context.Context,
	templateId string, args []string, number ...string) error {
	// 用下一个服务商
	idx := atomic.AddUint64(&f.idx, 1)
	length := uint64(len(f.svcs))
	// 从 idx 开始，迭代 length 次
	for i := idx; i < idx+length; i++ {
		// 取余数来计算下标（因为 idx+length 是有可能大于 length 的）
		svc := f.svcs[i%length]
		err := svc.Send(ctx, templateId, args, number...)
		switch err {
		case nil:
			return nil
		case context.Canceled, context.DeadlineExceeded: // 调用者主动取消调用 / 超时
			return err
		}
		// 其他情况会走到这里，需要打日志
		log.Println(err)
	}
	return errors.New("轮询了所有短信服务商，都发送失败了")
}
